using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using XLFrame.NodeEditor.Extensions;

namespace XLFrame.NodeEditor
{
    /// <summary>
    /// 逻辑体抽象类  获取的输入数据如果是null,通知对方输出端口刷新数据
    /// </summary>
    public abstract class NodeLogicBase : INodeLogic
    {
        // 输入数据
        public List<PortData> inputDatas = new List<PortData>();
        private Dictionary<string, PortData> inputDatashc = new Dictionary<string, PortData>();

        // 返回数据
        public List<PortData> outputDatas = new List<PortData>();
        private Dictionary<string, PortData> outputDatashc = new Dictionary<string, PortData>();

        /// <summary>
        /// 更新输入数据
        /// </summary>
        private Func<List<PortData>> inputFunc;
        private Func<List<PortData>> outputFunc;

        /// <summary>
        /// 调用下一个节点,参数是自身要执行的端口 GUID 
        /// </summary>
        protected UnityAction<string> onNextExecute;

        /// <summary>
        /// 当逻辑执行结束时调用
        /// </summary>
        public event UnityAction<INodeLogic> OnExecuteEnd;

        /// <summary>
        /// 当前执行的任务索引
        /// </summary>
        public string EGuid
        {
            get => m_eGuid;
            set
            {
                m_oldEGuid = m_eGuid;
                m_eGuid = value;
            } 
        }

        private string m_eGuid;
        private string m_oldEGuid;

        /// <summary>
        /// 当其他节点获取输出数据时调用
        /// </summary>
        public event Func<string,object,string,object> OnGetOutputDataEvent;
        
        
        /// <summary>
        /// 节点的数据可以访问内部的Attributes
        /// </summary>
        public NodeDataBase NodeData { get; private set; }


        public NodeLogicBase()
        {
            OnGetOutputDataEvent += OnGetOutputDataEventFunc;
        }

        private object OnGetOutputDataEventFunc(string arg1, object arg2, string arg3)
        {
            EGuid = arg3;
            return OnGetOutputData(arg1, arg2, arg3);
        }

        /// <summary>
        /// 当其他节点尝试获取输出值时,返回的object就是其他节点拿到的值
        /// </summary>
        /// <param name="arg1">参数名</param>
        /// <param name="arg2">参数值</param>
        /// <param name="eGuid"></param>
        /// <returns>你要返回的参数值</returns>
        protected virtual object OnGetOutputData(string arg1, object arg2,string eGuid)
        {
            return arg2;
        }
        /// <summary>
        /// 被调用时,执行逻辑
        /// </summary>
        /// <param name="lineName">走的逻辑线名称</param>
        protected abstract void OnExecute(string lineName);
        
        
        

        public void RefreshData()
        {
            foreach (var item in outputDatas)
            {
                item.OnGetDataEvent -= OnGetOutputDataEvent;
            }
            
            outputDatas = outputFunc.Invoke();
            inputDatas  = inputFunc.Invoke();
            ComputationalCache(outputDatashc, outputDatas);
            ComputationalCache(inputDatashc, inputDatas);
            
            foreach (var item in outputDatas)
            {
                item.OnGetDataEvent += OnGetOutputDataEvent;
            }
        }

        /// <summary>
        /// 获取输入数据,获取输出数据,执行下一个节点逻辑,参数是自身要执行的端口 GUID 
        /// </summary>
        /// <param name="input"></param>
        /// <param name="output"></param>
        /// <param name="onNextExecute"></param>
        /// <param name="nodeData"></param>
        public void Init(NodeDataBase nodeData,Func<List<PortData>> input, Func<List<PortData>> output, UnityAction<string> onNextExecute)
        {
            inputFunc = input;
            outputFunc = output;
            this.onNextExecute = onNextExecute;
            NodeData = nodeData;
            RefreshData();
        }

        /// <summary>
        /// 计算端口数据缓存
        /// </summary>
        /// <param name="hc"></param>
        /// <param name="data"></param>
        public void ComputationalCache(Dictionary<string, PortData> hc, List<PortData> data)
        {
            hc.Clear();
            foreach (var item in data)
            {
                hc.Add(ToolExtensions.RemoveNameType(item.NameType), item);
            }
        }

        /// <summary>
        /// 查找输入数据
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private PortData FindInputData(string name)
        {
            return inputDatashc.GetValueOrDefault(name);
        }
        /// <summary>
        /// 查找输出数据
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private PortData FindOutputData(string name)
        {
            if (outputDatashc.TryGetValue(name, out PortData portData))
            {
                return portData;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取输出端口GUID
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetOutputPortGUID(string name)
        {
            return FindOutputData(name)?.GUID;
        }

        /// <summary>
        /// 获取输入端口GUID
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetInputPortGUID(string name)
        {
            return FindInputData(name)?.GUID;
        }

        /// <summary>
        /// 获取输入数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public T GetInputData<T>(string name)
        {
            var obj = FindInputData(name);
            
            if(obj is null)
            {
                Debug.Log("未找到名为:"+ name+"的输入数据");
                return default;
            }
            if(obj.Value is null)
                return default;

            try
            {
                if(obj.Value is PortData portData)
                {
                    return portData.GetValue(EGuid).Conversion<T>();
                }
                else
                {
                    Debug.LogError($"无法将输入数据'{name}:{obj.Value}'的值转换为类型'{typeof(T).Name}'。");
                    return default;
                }
            }
            catch (InvalidCastException)
            {
                Debug.LogError($"无法将输入数据'{name}'的值转换为类型'{typeof(T).Name}'。");
                return default;
            }
        }

        /// <summary>
        /// 设置返回数据
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public void SetOutputData(string name, object value)
        {
            var obj = FindOutputData(name);
            if(obj is null)
            {
                Debug.Log("未找到名为:"+ name+"的输出数据");
                return;
            }
            obj.Value = value;
        }

        /// <summary>
        /// 获取返回数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public T GetOutputData<T>(string name)
        {
            var obj = FindOutputData(name);
            if (obj is not null) return (T)obj.Value;
            Debug.LogError("未找到名为:"+ name+"的输出数据");
            return default;
        }

        public void Execute(string lineName)
        {
            // 这里修改成连线就刷新输入端数据
            OnExecute(lineName);
            OnExecuteEnd?.Invoke(this);

            // 默认查找执行端口.并调用下一个节点
            var guid = GetOutputPortGUID("执行");
            if (!string.IsNullOrEmpty(guid))
            {
                onNextExecute?.Invoke(guid);
            }

        }

       
    }

    /// <summary>
    /// 端口数据,这个类不需要被序列化保存,属于运行时数据
    /// 编写解释器时,根据节点存档的端口数据和连接数据建立对应的PortData即可,
    /// 相当于反加载时,不再用节点,而是自定义的数据类型,里面保存端口信息逻辑信息连接信息
    /// </summary>
    public class PortData : IPropertyChanged
    {
        public PortData(string nameType, string guid, string type,AttributesData attributesData)
        {
            NameType = nameType;
            GUID = guid;
            Type = type;
            m_attributesData = attributesData;

            // 加载默认值
            if (m_attributesData.ContainsKey(GUID))
            {
                Value = m_attributesData.GetAttribute<object>(GUID);
                IsSave = true;
            }
            
            OnGetDataEvent += OnGetDataFunc;
            OnPropertyChangeEvent += OnPropertyChange;
        }

     

        AttributesData m_attributesData;
        
        /// <summary>
        /// 端口唯一id
        /// </summary>
        public string GUID { get; private set; }
        
        /// <summary>
        /// 是否保存数据
        /// </summary>
        public bool IsSave { get; set; } = false;

        /// <summary>
        /// 端口显示名称
        /// </summary>
        public string NameType
        {
            get=>m_nameType;
            private set
            {
                m_nameType = value;
                Name = ToolExtensions.RemoveNameType(m_nameType);
            }
        }
        private string m_nameType;
        
        /// <summary>
        /// 去掉类型的名称
        /// </summary>
        public string Name { get;private set; }
       

        /// <summary>
        /// 端口数据类型
        /// </summary>
        public string Type { get; private set; }
        
        /// <summary>
        /// 当前执行的任务索引
        /// </summary>
        public string EGuid {
            get=> m_eGuid;
            set
            {
                m_oldEGuid = m_eGuid;
                m_eGuid = value;
            } }
        private string m_oldEGuid;
        private string m_eGuid;
        
        /// <summary>
        /// 是否是输入端口数据(内部装载已连接的输出端PortData)
        /// </summary>
        public bool IsInputPort => Value is PortData;

        /// <summary>
        /// 端口值
        /// </summary>
        public object Value
        {
            get
            {
                if (m_oldEGuid != m_eGuid)
                {
                    var obj = OnGetDataEvent?.Invoke(Name, m_value, EGuid);
                    if (obj != m_value)
                        Value = obj;
                    return obj;
                }
                else
                {
                    return m_value;
                }
            }
            set
            {
                if(m_value is IPropertyChanged oldIPropertyChanged)
                {
                    oldIPropertyChanged.OnPropertyChangeEvent -= OnPropertyChangeEventInvoke;
                }
                if(value is IPropertyChanged iPropertyChanged)
                {
                    iPropertyChanged.OnPropertyChangeEvent += OnPropertyChangeEventInvoke;
                }
                
                m_value = value;
                
                OnPropertyChangeEventInvoke(m_portDataEvent.Init(this, m_value, 
                                                                 m_value,Name) );
            }
        }
        private object m_value;
        private PortDataEvent m_portDataEvent = new PortDataEvent();

        public object GetValue(string EGuid)
        {
            this.EGuid = EGuid;
            return Value;
        }
        
        /// <summary>
        /// 内部属性发生变化
        /// </summary>
        /// <param name="obj">发生变化的事件值</param>
        private void OnPropertyChangeEventInvoke(PortDataEvent obj)
        {
            OnPropertyChangeEvent?.Invoke(m_portDataEvent.Init(this, m_value,
                                                               obj.ChangeValue, obj.ChangeValueName));
        }

        /// <summary>
        /// 当值改变时
        /// </summary>
        public event UnityAction<PortDataEvent> OnPropertyChangeEvent;
        
        /// <summary>
        /// 当有人获取数据时[string ValueName,object Value,string EGuid,返回给我的值]
        /// </summary>
        public event Func<string,object,string,object> OnGetDataEvent;
        
        /// <summary>
        /// 当值改变时,保存数据,仅保存输出端口的值
        /// </summary>
        /// <param name="arg0"></param>
        private void OnPropertyChange(PortDataEvent arg0)
        {
            if (IsSave)
            {
                if(IsInputPort)
                    return;
                m_attributesData.SetAttribute(GUID, m_value);
            }
        }

        /// <summary>
        /// [注册自OnGetDataEvent]
        /// 默认直接返回
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <param name="eGuid"></param>
        /// <returns></returns>
        private object OnGetDataFunc(string arg1, object arg2,string eGuid)
        {
            // EGuid = eGuid;
            return arg2;
        }
    }
    /// <summary>
    /// 用于给事件传参
    /// </summary>
    public class PortDataEvent
    {
        /// <summary>
        /// 发生变化的PortData类 可以查到对应的端口信息
        /// </summary>
        public PortData ChangeContainer { get;private set; }
            
        /// <summary>
        /// 发生变化的对象 存放于PortData中的Value
        /// </summary>
        public object ChangeObject { get;private set; }
            
        /// <summary>
        /// 发生变化的值 存放于PortData中的Value中的字段或者属性
        /// </summary>
        public object ChangeValue { get;private set; }
            
        /// <summary>
        /// 发生变化的字段名称
        /// </summary>
        public string ChangeValueName { get;private set; }
            
        /// <summary>
        /// 
        /// </summary>
        /// <param name="changeContainer">发生变化的PortData类 可以查到对应的端口信息</param>
        /// <param name="changeObject">发生变化的对象 存放于PortData中的Value</param>
        /// <param name="changeValue">发生变化的值 存放于PortData中的Value中的字段或者属性</param>
        /// <param name="changeValueName">发生变化的字段名称</param>
        /// <returns></returns>
        public PortDataEvent Init(PortData changeContainer, object changeObject, object changeValue, string changeValueName)
        {
            ChangeContainer = changeContainer;
            ChangeObject    = changeObject;
            ChangeValue     = changeValue;
            ChangeValueName = changeValueName;
            return this;
        }
    }
}
